﻿/**********************************************************/
/*                Code by: Sam Wronski                    */
/*             email: runewake2@gmail.com                 */
/* Distributed under the Microsoft Public License (Ms-PL) */
/**********************************************************/
/*     https://gamejolttrophies.codeplex.com/license      */
/**********************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.ComponentModel;

namespace GameJoltAPI
{
    public class TrophyEventArgs : EventArgs
    {
        private List<Trophy> trophies;

        public List<Trophy> Trophies { get { return trophies; } set { trophies = value; } }

        public TrophyEventArgs(List<Trophy> t) { this.trophies = t; }
    }

    public delegate void TrophyEventHandler(object sender, TrophyEventArgs e);

    /// <summary>
    /// Handles Fetching and Unlocking Trophies
    /// </summary>
    public class TrophyClient
    {
        //The base URL used to get the list of games, note that format is explicitly defined here
        private static readonly string _url_fetch = @"http://gamejolt.com/api/game/v1/trophies/?format=json";
        private static readonly string _url_achieve = @"http://gamejolt.com/api/game/v1/trophies/add-achieved/?format=dump";

        /// <summary>
        /// <para>This event occurs when a list of trophies has been successfully fetched.</para>
        /// </summary>
        public event TrophyEventHandler OnFetchComplete;

        /// <summary>
        /// <para>This event occurs whenever an error occurs when fetching trophies.</para>
        /// </summary>
        public event EventHandler OnFetchError;

        /// <summary>
        /// <para>This event is called when an Achievement is successfully unlocked.</para>
        /// </summary>
        public event EventHandler OnAchievementComplete;

        /// <summary>
        /// <para>This event is called whenever an error occurs when attempting to achieve a trophy.</para>
        /// <para>This events sender will be a string containing the error message.</para>
        /// </summary>
        public event EventHandler OnAchievementError;

        /// <summary>
        /// Finds a list of trophies with the specified ID's
        /// </summary>
        /// <param name="tid">the list of Trophy ID's to fetch</param>
        public void FetchTrophies(List<string> tid)
        {
            string req = null;
            if (tid != null)
            {
                foreach (string s in tid)
                    req += s + ",";
                req = req.Substring(0, req.Count() - 1);
            }

            FetchTrophies(null, req);
        }

        /// <summary>
        /// Finds a list of trophies, which trophies depends on the parameters
        /// </summary>
        /// <param name="achieved">Set to "true" to return only the achieved trophies for a user or "false" to 
        /// return only trophies the user hasn't achieved yet. Leave this blank to retrieve all trophies.</param>
        /// <param name="tid"><para>If you would like to return just one trophy, you may pass the trophy ID with
        /// this parameter. If you do, only that trophy will be returned in the response.</para><para>You may also pass
        /// multiple trophy IDs here if you want to return a subset of all the trophies - you do this as a
        /// comma separated list in the same way you would retrieving multiple users.</para><para>Passing a trophy_id
        /// or a set of trophy_ids will ignore the "achieved" parameter if it is passed.</para>
        /// <example>1111,2222,3333,4444</example></param>
        public void FetchTrophies(bool? achieved = null, string tid = null)
        {
            string url = _url_fetch;

            url += "&game_id=" + Settings.GameID;
            url += "&username=" + Settings.Username;
            url += "&user_token=" + Settings.Token;
            if (achieved != null)
                url += "&achieved=" + (achieved == true ? "true" : "false");
            if (tid != null)
                tid += "&trophy_id=" + tid;

            BeginFetchResult(url);
        }

        /// <summary>
        /// Achieves a specific trophy for the current player
        /// </summary>
        /// <param name="trophy">The current player</param>
        public void AchieveTrophy(string trophy)
        {
            string url = _url_achieve;
            url += "&game_id=" + Settings.GameID;
            url += "&username=" + Settings.Username;
            url += "&user_token=" + Settings.Token;
            url += "&trophy_id=" + trophy;

            BeginAchievedResult(url);
        }

        //This gets the result
        private void BeginFetchResult(string url)
        {
            string rs = url + "&signature=" + Settings.Signiture(url); //Append signiture

            NetworkAccess.StartConnection();

            //TODO: Web API Call
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(rs);
            request.Method = "GET";
            request.BeginGetResponse(EndFetchResult, request);
        }

        //This fetches the result data
        private void EndFetchResult(IAsyncResult result)
        {
            NetworkAccess.FinishConnection();

            if (result.IsCompleted)
            {
                HttpWebRequest request = (HttpWebRequest)result.AsyncState;

                //Deserialize the data and call the event
                string data = new StreamReader(request.EndGetResponse(result).GetResponseStream()).ReadToEnd();

                TrophyResult res = JsonConvert.DeserializeObject<TrophyResult>(data);
                CallFetchCompleted(res);
            }
        }

        //This interprets the result data
        private void CallFetchCompleted(TrophyResult result)
        {
            if (result != null && result.Response.Success.ToLower()[0] == 't')
            {
                if (OnFetchComplete != null)
                    OnFetchComplete(result, new TrophyEventArgs(result.Response.Trophies));
            }
            else
            {
                if (OnFetchError != null)
                    OnFetchError(result, new EventArgs());
            }
        }

        //This gets the result
        private void BeginAchievedResult(string url)
        {
            string rs = url + "&signature=" + Settings.Signiture(url); //Append signiture

            NetworkAccess.StartConnection();

            //TODO: Web API Call
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(rs);
            request.Method = "GET";
            request.BeginGetResponse(EndAchievedResult, request);
        }

        //This fetches the result data
        private void EndAchievedResult(IAsyncResult result)
        {
            NetworkAccess.FinishConnection();

            if (result.IsCompleted)
            {
                HttpWebRequest request = (HttpWebRequest)result.AsyncState;

                //Deserialize the data and call the event
                string data = new StreamReader(request.EndGetResponse(result).GetResponseStream()).ReadToEnd();

                if (data.ToLower()[0] == 's')
                {
                    if (OnAchievementComplete != null)
                        OnAchievementComplete(data, new EventArgs());
                }
                else
                {
                    if (OnAchievementError != null)
                        OnAchievementError(data, new EventArgs());
                }
            }
        }
    }

    [JsonObject()]
    public class TrophyResult
    {
        [JsonProperty("response")]
        public TrophyResponse Response { get; set; }
    }

    [JsonObject()]
    public class TrophyResponse
    {
        [JsonProperty("success")]
        public string Success { get; set; }

        [JsonProperty("trophies")]
        public List<Trophy> Trophies { get; set; }
    }

    /// <summary>
    /// Represents a Trophy and contains it's relevant data.
    /// This data may be set under Manage Achievements in your Game Dashboard.
    /// </summary>
    [JsonObject()]
    public class Trophy
    {
        /// <summary>
        /// The Achievements ID
        /// </summary>
        [JsonProperty("id")]
        public string ID { get; set; }

        /// <summary>
        /// The Title of the Achievement, its name
        /// </summary>
        [JsonProperty("title")]
        public string Title { get; set; }

        /// <summary>
        /// A desctiption of the Achievement
        /// </summary>
        [JsonProperty("description")]
        public string Description { get; set; }

        /// <summary>
        /// The Difficulty of the Achievement, as a String
        /// </summary>
        [JsonProperty("difficulty")]
        public string DifficultyString { get; set; }

        /// <summary>
        /// The URL of the Achievements Image
        /// </summary>
        [JsonProperty("image_url")]
        public string ImageURL { get; set; }

        /// <summary>
        /// The Achievement Status of the achievement as a string. That is, "true" or "false"
        /// </summary>
        [JsonProperty("achieved")]
        public string AchievedString { get; set; }

        /// <summary>
        /// Gets the Achievement Status of the achievement
        /// </summary>
        public bool Achieved { get { return AchievedString.ToLower()[0] == 't' ? true : false; } }

        /// <summary>
        /// Achieves the trophy for the current player if they have not already achieved it.
        /// </summary>
        /// <param name="tc">The TrophyClient to use for Achieving the Achievement</param>
        public void Achieve(TrophyClient tc)
        {
            if (!Achieved)
            {
                tc.AchieveTrophy(ID);
            }
        }
    }
}
